using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace Crazy_Crafter
{
    class MemoryManager
    {
#region Pointers
// Updated 6 Sep 11 - Still Missing Character Data

        const int PointerBaseWindow = 0x00E96A08;
        const int PointerOffsetWindow = 0x15;
        const int PointerPlayerStructure = 0xD9EC78;

        const int PointerChatBase = 0x00EA04A0;
        const int PointerChatOffset = 0x65C;

        const int PointerNameBase = 0x00E966B8;

        const int SelectedItemCraft = 0x00D907A8; // Not Working
        int[] SelectedItemCraftOffset = {0xa0, 0x29c, 0x128 };

        int baseAddress;

        const bool CharDataBroke = true;
#endregion


        [DllImport("kernel32.dll")]
        public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpbaseWindow, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);

        [DllImport("User32")]
        private static extern int ShowWindow(int hwnd, int nCmdShow);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
        static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);

        Process Game;
        XIVWindow[] XIVWindows = new XIVWindow[32];

        public MemoryManager(Process p)
        {
            Game = new Process();
            Game = p;
        }

        public string GetCharacterName() {
            // Detect Name Entry
            int pointer1; IntPtr ot;
            byte[] b = new byte[4];
            // e403cc 420 e2
            pointer1 = getPointer(PointerNameBase, 0x4D4, true);
            pointer1 = getPointer(pointer1, 0xE2);

            b = new byte[64];

            // Get Character Name
            ReadProcessMemory(Game.Handle, (IntPtr)(pointer1), b, (uint)64, out ot);

            string n = ASCIIEncoding.ASCII.GetString(b);
            return n.Substring(0, n.IndexOf((char)0));
        
        }

        /*
            LoadWindows();

            // Detect Chat Log
            p = ReadProcessMemory(P[0].Handle, (IntPtr)(0x00D9E810 + (int)P[0].MainModule.BaseAddress), b, 4, out ot);
            pointer1 = BitConverter.ToInt32(b, 0);
            pointer1 += 0x64C;
            p = ReadProcessMemory(P[0].Handle, (IntPtr)(pointer1), b, 4, out ot);
            pointer1 = BitConverter.ToInt32(b, 0);
            pointer1 += 0x40;
            p = ReadProcessMemory(P[0].Handle, (IntPtr)(pointer1), b, 4, out ot);
            pointer1 = BitConverter.ToInt32(b, 0);
            baseChat = pointer1;
            
            return true;
*/
        public bool CheckGame()
        {
            if (Game.HasExited)
            {
                return false;
            }
            return true;
        }

        public void ChangeVisible(bool Hide = false)
        {
            if (Hide)
            {
                ShowWindow((int)Game.MainWindowHandle, 0);
            }
            else
            {
                ShowWindow((int)Game.MainWindowHandle, 4);
            }
        }

        public void LoadWindows()
        {
            try
            {
                // Detect Windows
                int pointer1 = getPointer(PointerBaseWindow, PointerOffsetWindow, true);

                for (int x = 0; x < 32; x++)
                {
                    IntPtr ot;
                    byte[] b = new byte[64];
                    byte[] c = new byte[64];
                    // Search Windows
                    int p = ReadProcessMemory(Game.Handle, (IntPtr)(pointer1 + (x * 0x180)), b, (uint)64, out ot);

                    for (int y = 0; y < 64; y++)
                    {
                        if (b[y] == 0)
                        {
                            c = new byte[y];
                            for (int z = 0; z < y; z++)
                            {
                                c[z] = b[z];
                            }
                            y = 65;
                            break;
                        }
                        if (b[y] == 5)
                        {
                            y = 65;
                        }
                    }

                    XIVWindows[x].tag = ASCIIEncoding.ASCII.GetString(c);
                    XIVWindows[x].start = pointer1 + (x * 0x180);
                }
            }
            catch (ExecutionEngineException e)
            {
                System.Windows.Forms.MessageBox.Show("Exception: \n" + e.Data);
            }
        }

        public XIVWindow getWindow(string name)
        {
            if (XIVWindows[0].start == 0)
            {
                LoadWindows();
            }

            for (int x = 0; x < 32; x++)
            {
                if (name.Equals(XIVWindows[x].tag))
                {
                    byte[] b = new byte[name.Length]; IntPtr ot;
                    ReadProcessMemory(Game.Handle, (IntPtr)XIVWindows[x].start, b, (uint)name.Length, out ot);
                    if (name.Equals(ASCIIEncoding.ASCII.GetString(b)))
                        return XIVWindows[x];
                    else
                        break;
                }
            }
            LoadWindows();
            return new XIVWindow();
        }

        public int getWindowData(string name, int offset, int size = 4)
        {
            XIVWindow W = getWindow(name);
            if (W.start == 0) return -1;
            byte[] b = new byte[(size >= 2 ? size : 2)]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)(W.start + offset), b, (uint)size, out ot);
            if (size == 1)
                return BitConverter.ToInt16(b, 0);
            else if (size == 2)
                return BitConverter.ToInt16(b, 0);
            else if (size == 4)
                return BitConverter.ToInt32(b, 0);
            else
                return -1;
        }

        public int getNumberData(int Address, int size = 4)
        {
            byte[] b = new byte[(size >= 2 ? size : 2)]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)(Address), b, (uint)size, out ot);
            if (size == 1)
                return BitConverter.ToInt16(b, 0);
            else if (size == 2)
                return BitConverter.ToInt16(b, 0);
            else if (size == 4)
                return BitConverter.ToInt32(b, 0);
            else
                return -1;
        }

        public string getTextData(int Address, int size = 0)
        {
            // Detect Name Entry
            IntPtr ot;
            byte[] b = new byte[(size > 0?size:1024)];

            // Get Character Name
            ReadProcessMemory(Game.Handle, (IntPtr)(Address), b, (uint)(size > 0 ? size : 1024), out ot);

            string n = ASCIIEncoding.ASCII.GetString(b);
            return n.Substring(0, n.IndexOf((char)0));
        }

        public byte[] getWindowBytes(string name)
        {
            XIVWindow W = getWindow(name);
            if (W.start == 0) return new byte[0];
            byte[] b = new byte[0x180]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)(W.start), b, (uint)0x180, out ot);
            return b;
        }

        public int GetJobLevel()
        {
            if (CharDataBroke) return 0;
            int level;
            byte[] b = new byte[2]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)(getPointer(PointerPlayerStructure, 0x8D, true)), b, 2, out ot);
            level = BitConverter.ToInt16(b, 0);
            return level;
        }

        public int GetJobID()
        {
            if (CharDataBroke) return 0;
            int id;
            byte[] b = new byte[2]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)(getPointer(PointerPlayerStructure, 0x89, true)), b, 2, out ot);
            id = BitConverter.ToInt16(b, 0);
            return id;
        }

        public int GetSelectedItemID()
        {
            int id;
            byte[] b = new byte[8]; IntPtr ot;

            int pointer = getPointer(SelectedItemCraft, SelectedItemCraftOffset[0], true);
            pointer = getPointer(pointer, SelectedItemCraftOffset[1]);
            pointer = getPointer(pointer, SelectedItemCraftOffset[2]);
            ReadProcessMemory(Game.Handle, (IntPtr)(pointer), b, 8, out ot);
            id = BitConverter.ToInt32(b, 4);
            if (id != 1098907648) return 0;
            ReadProcessMemory(Game.Handle, (IntPtr)(pointer), b, 4, out ot);
            id = BitConverter.ToInt32(b, 0);
            return id;
        }

        public int GetChatLineNumber()
        {
            int id;
            byte[] b = new byte[4]; IntPtr ot;
            int pointer = getPointer(PointerChatBase, PointerChatOffset, true);
            pointer = getPointer(pointer, 0x10);
            ReadProcessMemory(Game.Handle, (IntPtr)(pointer), b, 4, out ot);
            id = BitConverter.ToInt32(b, 0);
            return id;
        }

        public string GetChatLine(int line)
        {
            int end; int start = 0;
            byte[] b = new byte[128]; IntPtr ot;
            int pointer;

            pointer = getPointer(PointerChatBase, PointerChatOffset, true);
            pointer = getPointer(pointer, 0x30);
            pointer = getPointer(pointer, 0);
            if (line != 0)
            {
                ReadProcessMemory(Game.Handle, (IntPtr)(pointer + (line * 4) - 4), b, 8, out ot);
                end = BitConverter.ToInt32(b, 4);
                start = BitConverter.ToInt32(b, 0);
            }
            else
            {
                ReadProcessMemory(Game.Handle, (IntPtr)(pointer + (line * 4)), b, 4, out ot);
                end = BitConverter.ToInt32(b, 0);
            }

            pointer = getPointer(PointerChatBase, PointerChatOffset, true);
            pointer = getPointer(pointer, 0x40);
            pointer = getPointer(pointer, start);
            ReadProcessMemory(Game.Handle, (IntPtr)(pointer), b, (uint)(end - start), out ot);
            string s = ASCIIEncoding.ASCII.GetString(b);
            return s.Replace(Convert.ToChar(0x0).ToString(), "");
        }

        public string GetJobName()
        {
            if (CharDataBroke) return "Broken";
            int id = GetJobID();
            switch (id)
            {
                case 2: return "Pugilist";
                case 3: return "Gladiator";
                case 4: return "Marauder";
                case 7: return "Archer";
                case 8: return "Lancer";
                case 22: return "Thaumaturge";
                case 23: return "Conjurer";
                case 29: return "Carpenter";
                case 30: return "Blacksmith";
                case 31: return "Armorer";
                case 32: return "Goldsmith";
                case 33: return "Leatherworker";
                case 34: return "Weaver";
                case 35: return "Alchemist";
                case 36: return "Culinarian";
                case 39: return "Miner";
                case 40: return "Botonist";
                case 41: return "Fisher";
                default: return "";
            }
        }

        public struct XIVWindow
        {
            public int start;
            public string tag;
        }

        private int getPointer(int basepointer, int offset, bool raw = false)
        {
            uint bp = (uint)basepointer;
            if (raw) {
                if (baseAddress == 0) baseAddress = Game.MainModule.BaseAddress.ToInt32();
                bp += (uint)baseAddress;
            }
            byte[] b = new byte[4]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)(bp), b, 4, out ot);
            basepointer = BitConverter.ToInt32(b, 0);
            basepointer += offset;
            return basepointer;
        }

        public int GetJobExperience()
        {
            if (CharDataBroke) return 0;
            if (!CheckGame()) return 0;
            byte[] b = new byte[4]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)getPointer(PointerPlayerStructure, (0x73D + (GetJobID() * 4)), true), b, 4, out ot);
            return BitConverter.ToInt32(b, 0);
        }

        public int GetLevelExperience()
        {
            if (CharDataBroke) return 0;
            if (!CheckGame()) return 0;
            byte[] b = new byte[4]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)getPointer(PointerPlayerStructure, (0x66A), true), b, 4, out ot);
            return BitConverter.ToInt32(b, 0);
        }

        public int GetLevel()
        {
            if (CharDataBroke) return 0;
            if (!CheckGame()) return 0;
            byte[] b = new byte[2]; IntPtr ot;
            ReadProcessMemory(Game.Handle, (IntPtr)getPointer(PointerPlayerStructure, (0x666), true), b, 2, out ot);
            return BitConverter.ToInt16(b, 0);
        }

        public int GetJobMaxExperience(int LevelDiff = 0)
        {
            if (!CheckGame()) return 0;
            return SkillPoints(GetJobLevel() + LevelDiff);
        }

        public int GetLevelMaxExperience(int LevelDiff = 0)
        {
            if (!CheckGame()) return 0;
            return ExperiencePoints(GetLevel() + LevelDiff);
        }

        private int SkillPoints(int Level)
        {
            if (!CheckGame()) return 0;
            switch (Level)
            {
                case 1: return 570;
                case 2: return 700;
                case 3: return 880;
                case 4: return 1100;
                case 5: return 1500;
                case 6: return 1800;
                case 7: return 2300;
                case 8: return 3200;
                case 9: return 4300;
                case 10: return 5000;
                case 11: return 5900;
                case 12: return 6800;
                case 13: return 7700;
                case 14: return 8700;
                case 15: return 9700;
                case 16: return 11000;
                case 17: return 12000;
                case 18: return 13000;
                case 19: return 15000;
                case 20: return 16000;
                case 21: return 20000;
                case 22: return 22000;
                case 23: return 23000;
                case 24: return 25000;
                case 25: return 27000;
                case 26: return 29000;
                case 27: return 31000;
                case 28: return 33000;
                case 29: return 35000;
                case 30: return 38000;
                case 31: return 45000;
                case 32: return 47000;
                case 33: return 50000;
                case 34: return 53000;
                case 35: return 56000;
                case 36: return 59000;
                case 37: return 62000;
                case 38: return 65000;
                case 39: return 68000;
                case 40: return 71000;
                case 41: return 74000;
                case 42: return 78000;
                case 43: return 81000;
                case 44: return 85000;
                case 45: return 89000;
                case 46: return 93000;
                case 47: return 96000;
                case 48: return 100000;
                case 49: return 100000;
                case 50: return 110000;
                default:
                    return 0;
            }
        }

        private int ExperiencePoints(int Level)
        {
            if (!CheckGame()) return 0;
            switch (Level)
            {
                case 1: return 2700;
                case 2: return 3400;
                case 3: return 4400;
                case 4: return 5800;
                case 5: return 7600;
                case 6: return 9500;
                case 7: return 12000;
                case 8: return 14000;
                case 9: return 17000;
                case 10: return 20000;
                case 11: return 24000;
                case 12: return 28000;
                case 13: return 32000;
                case 14: return 36000;
                case 15: return 40000;
                case 16: return 45000;
                case 17: return 51000;
                case 18: return 57000;
                case 19: return 63000;
                case 20: return 69000;
                case 21: return 75000;
                case 22: return 82000;
                case 23: return 89000;
                case 24: return 97000;
                case 25: return 100000;
                case 26: return 110000;
                case 27: return 120000;
                case 28: return 130000;
                case 29: return 140000;
                case 30: return 150000;
                case 31: return 160000;
                case 32: return 170000;
                case 33: return 180000;
                case 34: return 190000;
                case 35: return 200000;
                case 36: return 210000;
                case 37: return 220000;
                case 38: return 230000;
                case 39: return 240000;
                case 40: return 260000;
                case 41: return 270000;
                case 42: return 280000;
                case 43: return 300000;
                case 44: return 310000;
                case 45: return 320000;
                case 46: return 340000;
                case 47: return 350000;
                case 48: return 370000;
                case 49: return 380000;
                case 50: return 400000;
                default:
                    return 0;
            }
        }

        public void SendDown()
        {
            ControlSendMessage(Game.MainWindowHandle, VirtualKeyStates.VK_DOWN, false);
        }

        public void SendUp()
        {
            ControlSendMessage(Game.MainWindowHandle, VirtualKeyStates.VK_UP, false);
        }

        public void SendEnter()
        {
            ControlSendMessage(Game.MainWindowHandle, VirtualKeyStates.VK_RETURN, false);
        }

        public void SendEscape()
        {
            ControlSendMessage(Game.MainWindowHandle, VirtualKeyStates.VK_ESCAPE, false);
        }

        #region USELESS SHIT
        #region Virtual Keys
        /// <summary>
        /// Virtual Keys
        /// </summary>
        public enum VirtualKeyStates : int
        {
            VK_LBUTTON = 0x01,
            VK_RBUTTON = 0x02,
            VK_CANCEL = 0x03,
            VK_MBUTTON = 0x04,
            //
            VK_XBUTTON1 = 0x05,
            VK_XBUTTON2 = 0x06,
            //
            VK_BACK = 0x08,
            VK_TAB = 0x09,
            //
            VK_CLEAR = 0x0C,
            VK_RETURN = 0x0D,
            //
            VK_SHIFT = 0x10,
            VK_CONTROL = 0x11,
            VK_MENU = 0x12,
            VK_PAUSE = 0x13,
            VK_CAPITAL = 0x14,
            //
            VK_KANA = 0x15,
            VK_HANGEUL = 0x15, /* old name - should be here for compatibility */
            VK_HANGUL = 0x15,
            VK_JUNJA = 0x17,
            VK_FINAL = 0x18,
            VK_HANJA = 0x19,
            VK_KANJI = 0x19,
            //
            VK_ESCAPE = 0x1B,
            //
            VK_CONVERT = 0x1C,
            VK_NONCONVERT = 0x1D,
            VK_ACCEPT = 0x1E,
            VK_MODECHANGE = 0x1F,
            //
            VK_SPACE = 0x20,
            VK_PRIOR = 0x21,
            VK_NEXT = 0x22,
            VK_END = 0x23,
            VK_HOME = 0x24,
            VK_LEFT = 0x25,
            VK_UP = 0x26,
            VK_RIGHT = 0x27,
            VK_DOWN = 0x28,
            VK_SELECT = 0x29,
            VK_PRINT = 0x2A,
            VK_EXECUTE = 0x2B,
            VK_SNAPSHOT = 0x2C,
            VK_INSERT = 0x2D,
            VK_DELETE = 0x2E,
            VK_HELP = 0x2F,
            //
            VK_LWIN = 0x5B,
            VK_RWIN = 0x5C,
            VK_APPS = 0x5D,
            //
            VK_SLEEP = 0x5F,
            //
            VK_NUMPAD0 = 0x60,
            VK_NUMPAD1 = 0x61,
            VK_NUMPAD2 = 0x62,
            VK_NUMPAD3 = 0x63,
            VK_NUMPAD4 = 0x64,
            VK_NUMPAD5 = 0x65,
            VK_NUMPAD6 = 0x66,
            VK_NUMPAD7 = 0x67,
            VK_NUMPAD8 = 0x68,
            VK_NUMPAD9 = 0x69,
            VK_MULTIPLY = 0x6A,
            VK_ADD = 0x6B,
            VK_SEPARATOR = 0x6C,
            VK_SUBTRACT = 0x6D,
            VK_DECIMAL = 0x6E,
            VK_DIVIDE = 0x6F,
            VK_F1 = 0x70,
            VK_F2 = 0x71,
            VK_F3 = 0x72,
            VK_F4 = 0x73,
            VK_F5 = 0x74,
            VK_F6 = 0x75,
            VK_F7 = 0x76,
            VK_F8 = 0x77,
            VK_F9 = 0x78,
            VK_F10 = 0x79,
            VK_F11 = 0x7A,
            VK_F12 = 0x7B,
            VK_F13 = 0x7C,
            VK_F14 = 0x7D,
            VK_F15 = 0x7E,
            VK_F16 = 0x7F,
            VK_F17 = 0x80,
            VK_F18 = 0x81,
            VK_F19 = 0x82,
            VK_F20 = 0x83,
            VK_F21 = 0x84,
            VK_F22 = 0x85,
            VK_F23 = 0x86,
            VK_F24 = 0x87,
            //
            VK_NUMLOCK = 0x90,
            VK_SCROLL = 0x91,
            //
            VK_OEM_NEC_EQUAL = 0x92, // '=' key on numpad
            //
            VK_OEM_FJ_JISHO = 0x92, // 'Dictionary' key
            VK_OEM_FJ_MASSHOU = 0x93, // 'Unregister word' key
            VK_OEM_FJ_TOUROKU = 0x94, // 'Register word' key
            VK_OEM_FJ_LOYA = 0x95, // 'Left OYAYUBI' key
            VK_OEM_FJ_ROYA = 0x96, // 'Right OYAYUBI' key
            //
            VK_LSHIFT = 0xA0,
            VK_RSHIFT = 0xA1,
            VK_LCONTROL = 0xA2,
            VK_RCONTROL = 0xA3,
            VK_LMENU = 0xA4,
            VK_RMENU = 0xA5,
            //
            VK_BROWSER_BACK = 0xA6,
            VK_BROWSER_FORWARD = 0xA7,
            VK_BROWSER_REFRESH = 0xA8,
            VK_BROWSER_STOP = 0xA9,
            VK_BROWSER_SEARCH = 0xAA,
            VK_BROWSER_FAVORITES = 0xAB,
            VK_BROWSER_HOME = 0xAC,
            //
            VK_VOLUME_MUTE = 0xAD,
            VK_VOLUME_DOWN = 0xAE,
            VK_VOLUME_UP = 0xAF,
            VK_MEDIA_NEXT_TRACK = 0xB0,
            VK_MEDIA_PREV_TRACK = 0xB1,
            VK_MEDIA_STOP = 0xB2,
            VK_MEDIA_PLAY_PAUSE = 0xB3,
            VK_LAUNCH_MAIL = 0xB4,
            VK_LAUNCH_MEDIA_SELECT = 0xB5,
            VK_LAUNCH_APP1 = 0xB6,
            VK_LAUNCH_APP2 = 0xB7,
            //
            VK_OEM_1 = 0xBA, // ';:' for US
            VK_OEM_PLUS = 0xBB, // '+' any country
            VK_OEM_COMMA = 0xBC, // ',' any country
            VK_OEM_MINUS = 0xBD, // '-' any country
            VK_OEM_PERIOD = 0xBE, // '.' any country
            VK_OEM_2 = 0xBF, // '/?' for US
            VK_OEM_3 = 0xC0, // '`~' for US
            //
            VK_OEM_4 = 0xDB, // '[{' for US
            VK_OEM_5 = 0xDC, // '|' for US
            VK_OEM_6 = 0xDD, // ']}' for US
            VK_OEM_7 = 0xDE, // ''"' for US
            VK_OEM_8 = 0xDF,
            //
            VK_OEM_AX = 0xE1, // 'AX' key on Japanese AX kbd
            VK_OEM_102 = 0xE2, // "<>" or "|" on RT 102-key kbd.
            VK_ICO_HELP = 0xE3, // Help key on ICO
            VK_ICO_00 = 0xE4, // 00 key on ICO
            //
            VK_PROCESSKEY = 0xE5,
            //
            VK_ICO_CLEAR = 0xE6,
            //
            VK_PACKET = 0xE7,
            //
            VK_OEM_RESET = 0xE9,
            VK_OEM_JUMP = 0xEA,
            VK_OEM_PA1 = 0xEB,
            VK_OEM_PA2 = 0xEC,
            VK_OEM_PA3 = 0xED,
            VK_OEM_WSCTRL = 0xEE,
            VK_OEM_CUSEL = 0xEF,
            VK_OEM_ATTN = 0xF0,
            VK_OEM_FINISH = 0xF1,
            VK_OEM_COPY = 0xF2,
            VK_OEM_AUTO = 0xF3,
            VK_OEM_ENLW = 0xF4,
            VK_OEM_BACKTAB = 0xF5,
            //
            VK_ATTN = 0xF6,
            VK_CRSEL = 0xF7,
            VK_EXSEL = 0xF8,
            VK_EREOF = 0xF9,
            VK_PLAY = 0xFA,
            VK_ZOOM = 0xFB,
            VK_NONAME = 0xFC,
            VK_PA1 = 0xFD,
            VK_OEM_CLEAR = 0xFE,
            //
            VK_0 = 0x30,
            VK_1 = 0x31,
            VK_2 = 0x32,
            VK_3 = 0x33,
            VK_4 = 0x34,
            VK_5 = 0x35,
            VK_6 = 0x36,
            VK_7 = 0x37,
            VK_8 = 0x38,
            VK_9 = 0x39
        }

        ///summary> 
        /// Virtual Messages 
        /// </summary> 


        /// <summary>
        /// Sends a message to the specified window
        /// </summary>
        /// <param name="winTitle">string Title of window</param>
        /// <param name="Key">Virtual key code of the key to send</param>

        void ControlSendMessage(IntPtr hWnd, VirtualKeyStates Key, bool shift, int hold = 50)
        {
            if (shift == true)
            {
                //send shift down
                SendMessage(hWnd, 0x100, (int)VirtualKeyStates.VK_SHIFT, 0);
                //send key down
                SendMessage(hWnd, 0x100, (int)Key, 0);
                //sleep 50ms
                Sleeper(hold, hold / 2);
                //send key up
                SendMessage(hWnd, 0x101, (int)Key, 0);
                //send shift up
                SendMessage(hWnd, 0x101, (int)VirtualKeyStates.VK_SHIFT, 0);
            }

            else
            {
                //send key down
                SendMessage(hWnd, 0x100, (int)Key, 0);
                //sleep 50ms

                Sleeper(hold, hold / 2);
                //send key up
                SendMessage(hWnd, 0x101, (int)Key, 0);
            }
        }

        /// <summary>
        /// Sends a Keydown message(0x100) to the specified window with a Virtual Key
        /// </summary>
        /// <param name="winTitle">Window Title</param>
        /// <param name="Key">Virtual Key to Send</param>
        void KeyDown(IntPtr hWnd, VirtualKeyStates Key)
        {
            SendMessage(hWnd, 0x100, (int)Key, 0);
        }

        /// <summary>
        /// Sends a Keydup message(0x101) to the specified window with a Virtual Key
        /// </summary>
        /// <param name="winTitle">Window Title</param>
        /// <param name="Key">Virtual Key to Send</param>
        void KeyUp(IntPtr hWnd, VirtualKeyStates Key)
        {
            SendMessage(hWnd, 0x101, (int)Key, 0);
        }


        /// <summary> 
        /// Sends a message to the specified handle 
        /// </summary> 
        void _SendMessage(IntPtr handle, int Msg, int wParam, int lParam)
        {
            SendMessage(handle, Msg, wParam, lParam);
        }

        public void Sleeper(int fast, int var = 50)
        {
            Random r = new Random();
            if (fast < var) fast = var;
            for (int x = 0; x < (fast / 10); x++)
            {
                Thread.Sleep((fast + r.Next(var * 2) - var) / 10);
                System.Windows.Forms.Application.DoEvents();
            }
        }

#endregion
        #endregion
    }
}
